#include "papview.hpp"
#include "ui_papview.h"

PapView::PapView(Pix *pix, QWidget *parent) :
    QDockWidget(parent),
    ui(new Ui::PapView)
{
    ui->setupUi(this);
    this->pix = pix;
    diagramScene = new DiagramScene();
    ui->papView->setScene(diagramScene);
    highlightedAddress = 0;
}

PapView::~PapView()
{
    reset();
    delete ui;
}

void PapView::changeEvent(QEvent *e)
{
    QDockWidget::changeEvent(e);
    switch (e->type()) {
    case QEvent::LanguageChange:
        ui->retranslateUi(this);
        break;
    default:
        break;
    }
}

void PapView::createPap(ProgramManagement *programManagement)
{
    int ya = 0;
    int xa = 0;
    int y = ya;
    int x = xa;
    int w = 100;
    int h = 25;
    int ySpace = 25;
    int xSpace = 50;
    DiagramItem *previous = diagramScene->addNewItem(DiagramItem::StartEnd, "Start", w, h, x, y);
    diagramItems.insert(-2, previous);
    y += h + ySpace;
    previous = diagramScene->addNewItem(DiagramItem::Step, "Initialisierung", w, h, x, y);
    diagramItems.insert(-1, previous);
    y += h + ySpace;
    diagramScene->addNewArrow(diagramItems.value(-2), diagramItems.value(-1));
    int nestingLevel = 0;
    int key = 0;
    int firstProgramCounter = -20;

    for(int i = 0; i < programManagement->getCommandList()->size(); i++)
    {
	Command tempCommand = programManagement->getCommandList()->at(i);
	if(tempCommand.getCommandType() == Command::LABEL)
	{
	    nestingLevel++;
	    y = ya + h + ySpace;
	    x = (w + xSpace) * nestingLevel;
	    diagramScene->addNewItem(DiagramItem::Label, tempCommand.getLabel(), w, h, (w + xSpace) * nestingLevel, ya);
	    key = pix->getDecoder()->getLabelMap().value(tempCommand.getLabel());//tempCommand->getAddress();
	    QList<Command> *list = new QList<Command>();
	    commandMap.insert(key, list);
	    if(firstProgramCounter == -20)
	    {
		firstProgramCounter = key;
	    }
	}
	if(tempCommand.getCommandType() == Command::COMMAND)
	{
	    commandMap.find(key).value()->append(tempCommand);
	}
    }

    QMapIterator<int, QList<Command> *> itCommandMap(commandMap);

    nestingLevel = 0;
    while(itCommandMap.hasNext())
    {
	nestingLevel++;
	itCommandMap.next();
	QList<Command> *tempList = itCommandMap.value();
	y = ya + h + ySpace;
	x = (w + xSpace) * nestingLevel;
	for(int i = 0; i < tempList->size(); i++)
	{
	    DiagramItem::DiagramType di = DiagramItem::Step;
	    if(tempList->at(i).getCommand() == Command::DECFSZ || tempList->at(i).getCommand() == Command::INCFSZ || tempList->at(i).getCommand() == Command::BTFSC || tempList->at(i).getCommand() == Command::BTFSS)
	    {
		di = DiagramItem::Conditional;
	    }
	    DiagramItem *temp = diagramScene->addNewItem(di, tempList->at(i).getAssembler(), w, h, x, y);
	    diagramItems.insert(tempList->at(i).getAddress(), temp);
	    y += h + ySpace;
	}
    }

    QStack<int> stack;
    int programCounter = 0;
    int previousCounter = 0;
    int maxCounter = 0;
    QMapIterator<int, Command> itMaxCounter(*programManagement->getCommandMap());
    while(itMaxCounter.hasNext())
    {
	itMaxCounter.next();
	maxCounter = itMaxCounter.key() > maxCounter ? itMaxCounter.key() : maxCounter;
    }
    bool overstepGoTo = false;
    while(diagramItems.contains(programCounter) && programCounter < maxCounter)
    {
	previousCounter = programCounter;
	Command tempCommand = programManagement->getCommandMap()->value(programCounter);
	if(!overstepGoTo && tempCommand.getCommand() == Command::GOTO)
	{
	    programCounter = tempCommand.getArgument(Command::ELEVENBIT);
	    overstepGoTo = false;
	}
	else if(tempCommand.getCommand() == Command::CALL)
	{
	    programCounter = tempCommand.getArgument(Command::ELEVENBIT);
	    stack.push(previousCounter);
	}
	else if(tempCommand.getCommand() == Command::INCFSZ || tempCommand.getCommand() == Command::DECFSZ || tempCommand.getCommand() == Command::BTFSC || tempCommand.getCommand() == Command::BTFSS)
	{
	    bool temp2 = diagramScene->addNewArrow(diagramItems.value(programCounter), diagramItems.value(programCounter + 2));
	    if(temp2)
	    {
		overstepGoTo = true;
	    }
	    programCounter++;
	}
	else if(tempCommand.getCommand() == Command::RETURN || tempCommand.getCommand() == Command::RETFIE || tempCommand.getCommand() == Command::RETLW)
	{
	    programCounter = stack.pop() + 1;
	}
	else
	{
	    programCounter++;
	}
	bool temp = diagramScene->addNewArrow(diagramItems.value(previousCounter), diagramItems.value(programCounter));
	if(temp)
	{
	    overstepGoTo = true;
	}
    }
    diagramScene->addNewArrow(diagramItems.value(-1), diagramItems.value(firstProgramCounter));
}

void PapView::highlightCommand(int address)
{
    diagramItems.value(address)->setBrush(Qt::red);
    ui->papView->ensureVisible(diagramItems.value(address));
    highlightedAddress = address;
}

void PapView::unhighlightCommand(int address)
{
    diagramItems.value(address)->setBrush(Qt::white);
}

void PapView::reset()
{
    QMapIterator<int, DiagramItem*> it(this->diagramItems);
    while(it.hasNext())
    {
	it.next();
	delete it.value();
	this->diagramItems.remove(it.key());
    }
}

void PapView::resetToProgramStart()
{
    this->unhighlightCommand(highlightedAddress);
}

void PapView::on_papScale_currentIndexChanged(int index)
{
    double newScale = ((double) (index + 1)) * 10 / 100.0;
    QMatrix oldMatrix = ui->papView->matrix();
    ui->papView->resetMatrix();
    ui->papView->translate(oldMatrix.dx(), oldMatrix.dy());
    ui->papView->scale(newScale, newScale);
}

void PapView::onProgramCounterChanged(int programCounter)
{
    this->unhighlightCommand(highlightedAddress);
    this->highlightCommand(programCounter);
}
